console.log("this is flowchart util js.");
var PROCESS_CONSTANTS = {
    WF_NODE_TYPE_START: 0,
    WF_NODE_TYPE_TASK: 1,
    WF_NODE_TYPE_END: 2,
    WF_NODE_TYPE_EXC_BRANCH: 3,
    WF_NODE_TYPE_PARA_BRANCH: 4,
    WF_NODE_TYPE_EVENT: 4,


    WF_APPR_AUTH_TYPE_DEPT: 0,
    WF_APPR_AUTH_TYPE_USER: 1,
    WF_APPR_AUTH_TYPE_ROLE: 2,
    WF_APPR_AUTH_TYPE_MY_DEPT: 3,
    WF_APPR_AUTH_TYPE_UP_DEPT: 4,
    WF_APPR_AUTH_TYPE_ALL_UP_DEPT: 5,

    WF_APPRULE_HUIQIAN: 1, //会签
    WF_APPRULE_HUOQIAN: 0, //或签

    SUCCESS_RULE_UNANIMOUSLY: 2, //全票通过
}
var PROCESS_CONFIG = {
    disabled: false,
}
var activeNodeClassName = "active-node";

var permissionHolder = (function () {
    function PermissionHolder() {
        this.holder = {};

        this.put = function (k, arr) {
            this.holder[k + ""] = arr;
        }

        this.get = function (k) {
            return this.holder[k + ""];
        }

        this.remove = function (k) {
            var v = this.get(k);
            delete this.holder[k];
            return v;
        }
        this.getData = function () {
            var arr = [];
            for (var id in this.holder) {
                var nArr = this.holder[id];
                if (nArr) {
                    for (var i = 0; i < nArr.length; i++) {
                        var n = nArr[i];
                        n.nodeid = id;
                        arr.push(n);
                    }
                }
            }

            return arr;
        }
    }
    return new PermissionHolder();
})();

function S4() {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}

function guid() {
    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}

function initFlowchart(instance, helper, nodes, connections, canvasElement) {

    console.log("fire  ", instance, helper);

    // make all the window divs draggable
    //instance.draggable(jsPlumb.getSelector(".flowchart-demo .node"), { grid: [20, 20] });
    // THIS DEMO ONLY USES getSelector FOR CONVENIENCE. Use your library's appropriate selector
    // method, or document.querySelectorAll:
    //jsPlumb.draggable(document.querySelectorAll(".node"), { grid: [20, 20] });

    // connect a few up

    var nodeGroupByDeep = {};
    var deepArr = []; //deep 按从小到大排序，-1排最后
    for (var i = 0; i < nodes.length; i++) {
        var node = nodes[i];
        var deep = node.deep;
        var arr = nodeGroupByDeep[deep];
        if (!arr) {
            arr = nodeGroupByDeep[deep] = [];
            if (deepArr.length && deep != -1) {
                for (var j = 0; j < deepArr.length; j++) {
                    var $deep = deepArr[j];
                    if (deep < $deep || $deep == -1) {
                        deepArr.splice(j, 0, deep);
                        break;
                    } else if (j == deepArr.length - 1) {
                        deepArr.push(deep);
                        break;
                    }
                }
            } else {
                deepArr.push(deep);
            }
        }
        arr.push(node);
    }

    console.log(nodeGroupByDeep, deepArr);
    for (var i = 0; i < deepArr.length; i++) {
        var deep = deepArr[i];
        var arr = nodeGroupByDeep[deep];
        var row = helper.newNodeRow(deep);
        $(canvasElement || ".flowchart-demo").append(row);

        arr.sort(function (a, b) {
            return a.serino > b.serino ? 1 : a.serino == b.serino ? 0 : -1;
        });
        for (var j = 0; j < arr.length; j++) {
            var data = arr[j];
            var node = helper.newNode(data);
            row.appendChild(node);
        }
    }


    var maxDeep = 0;

    instance.batch(function () {
        if (nodes) {
            for (var i = 0; i < nodes.length; i++) {
                var node = nodes[i];
                var serino = node.serino;

                helper.addEndpoints(instance, node);
            }
        }
    });
    instance.batch(function () {
        if (connections) {
            for (var i = 0; i < connections.length; i++) {
                helper._connect(instance, connections[i]);
            }
        }
    });

    $(document.body).off("click", ".node").on("click", ".node", function () {
        if (PROCESS_CONFIG.disabled) {
            return;
        }
        var jqThis = $(this);
        if (!jqThis.hasClass(activeNodeClassName)) {
            helper.clearElementActiveState(instance);
        }
        jqThis.toggleClass(activeNodeClassName);
        if (jqThis.hasClass(activeNodeClassName)) {
            helper.editor.editNode();
        }
    });

    instance.bind("msgConnectionExisted", function (a) {
        console.log(a, "fire repeat connection.")
    })
    instance.bind("msg", function (msg) {
        alert(msg);
    });

}

function getJsPlumbInstance(onJsPlumbInstanceReady){
    var _Top = "Top";
    var _Right = "Right";
    var _Bottom = "Bottom";
    var _Left = "Left";

    var __init = false;
    var ExportObjects = {
        
    };
    jsPlumb.ready(function () {
        
        if(__init){
            return onJsPlumbInstanceReady(ExportObjects);
        }
        __init = true;
        var instance = ExportObjects.instance = jsPlumb.getInstance({
            // default drag options
            DragOptions: {
                cursor: 'pointer',
                zIndex: 2000
            },
            // the overlays to decorate each connection with.  note that the label overlay uses a function to generate the label text; in this
            // case it returns the 'labelText' member that we set on each connection in the 'init' method below.
            ConnectionOverlays: [
                ["Arrow", {
                    location: 1,
                    visible: true,
                    width: 15,
                    length: 18,
                    id: "ARROW",
                }],
                ["Label", {
                    location: 0.3,
                    id: "label",
                    cssClass: "aLabel",
                    visible: false,
                }]
            ],
            Container: "canvas"
        });
    
    
        var activeConnectionTypeName = "basic";
        var basicType = {
            paintStyle: {
                stroke: "#2270f5",
                strokeWidth: 2
            },
        };
        instance.registerConnectionType("basic", basicType);
    
        // this is the paint style for the connecting lines..
        var connectorPaintStyle = {
                strokeWidth: 2,
                stroke: "#c4d7f7",
                joinstyle: "round",
                outlineStroke: "white",
                outlineWidth: 2
            },
            // .. and this is the hover style.
            connectorHoverStyle = {
                strokeWidth: 2,
                stroke: "#216477",
                outlineWidth: 5,
                outlineStroke: "white"
            },
            endpointHoverStyle = {
                fill: "#216477",
                stroke: "#216477"
            },
            // the definition of source endpoints (the small blue ones)
            sourceEndpoint = {
                endpoint: "Dot",
                paintStyle: {
                    stroke: "#7AB02C",
                    fill: "transparent",
                    radius: 7,
                    strokeWidth: 1
                },
                isSource: true,
                connector: ["Flowchart"],
                connectorStyle: connectorPaintStyle,
                hoverPaintStyle: endpointHoverStyle,
                connectorHoverStyle: connectorHoverStyle,
                dragOptions: {},
                maxConnections: -1,
    
            },
            // the definition of target endpoints (will appear when the user drags a connection)
            targetEndpoint = {
                endpoint: "Dot",
                paintStyle: {
                    fill: "#7AB02C80",
                    radius: 7
                },
                hoverPaintStyle: endpointHoverStyle,
                maxConnections: -1,
                dropOptions: {
                    hoverClass: "hover",
                    activeClass: "active"
                },
                isTarget: true,
    
            }
    
    
        function _initEvents() {
    
    
            instance.batch(function () {
    
                // listen for new connections; initialise them the same way we initialise the connections at startup.
                instance.bind("mouseover", function (connection) {
                    var label = connection.getOverlay("label");
                    console.log(label);
                    if (label) {
                        label.setVisible(true);
                    }
                });
                instance.bind("mouseout", function (connection) {
                    var label = connection.getOverlay("label");
                    console.log(label);
                    if (label) {
                        label.setVisible(false);
                    }
                });
    
                instance.bind("beforeStartDetach", function (connection) {
                    console.log(connection, "beforeStartDetach");
                    if (PROCESS_CONFIG.disabled) {
                        return false;
                    }
                });
    
                instance.bind("beforeDrop", function (info) {
                    var existed = _existConnection(instance, info.connection);
                    var self = info.connection.sourceId == info.connection.targetId;
                    if (existed) {
                        console.log("connection from " + info.connection.sourceId + " to " + info.connection.targetId + " existed. so , failed connect.");
                        instance.fire("msgConnectionExisted", info);
                    }
                    return !existed && !self;
                });
                //
                // listen for clicks on connections, and offer to delete connections on click.
                //
                instance.bind("click", function (conn, originalEvent) {
                    if (PROCESS_CONFIG.disabled) {
                        return;
                    }
                    // if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
                    //   instance.detach(conn);
                    if (!conn.hasType(activeConnectionTypeName)) {
                        _clearElementActiveState(instance);
                    }
                    conn.toggleType(activeConnectionTypeName);
                    if (conn.hasType(activeConnectionTypeName)) {
                        _editor.editConnection();
                        conn.addClass("jtk-active");
                    }
                });
    
    
                instance.bind("connectionDrag", function (connection) {
                    console.log("connection " + connection.id + " is being dragged. suspendedElement is ", connection.suspendedElement, " of type ", connection.suspendedElementType);
                });
    
                instance.bind("connectionDragStop", function (connection) {
                    console.log("connection " + connection.id + " was dragged");
                });
    
                instance.bind("connectionMoved", function (params) {
                    console.log("connection " + params.connection.id + " was moved");
                });
    
                
                instance.bind("connection", function (connInfo, originalEvent) {
                    _editor.applyConnectionData([connInfo.connection]);
                });
            });
        }
    
    
        _initEvents();
        /**页面操作的代码Start**/
        var nodeRowClassName = "node-row";
        var nodeClassName = "node";
        var startNodeRowClassName = "node-row-start";
        var endNodeRowClassName = "node-row-end";
    
    
    
        function _Editor() {
            this.nodes = [];
            this.connections = [];
            this.getActiveNode = function () {
                return $("." + nodeClassName + ".active-node");
            }
            this.getActiveConnection = function () {
                var conns = instance.getAllConnections();
                for (var i = 0; i < conns.length; i++) {
                    var conn = conns[i];
                    if (conn.hasType(activeConnectionTypeName)) {
                        return conn;
                    }
                }
            }
    
            this.editNode = function () {
                var activeNode = this.getActiveNode();
                if (!activeNode.length) {
                    instance.fire("msg", "请选中一个节点")
                    return;
                }
                jsPlumb.fire("editNode", activeNode);
            }
            this.editConnection = function () {
                var activeConn = this.getActiveConnection();
                if (!activeConn) {
                    instance.fire("msg", "请选中连接线");
                    return;
                }
                jsPlumb.fire("editConnection", activeConn);
            }
            this.updateNodeData = function (data) {
                var activeNode = this.getActiveNode();
                if (!activeNode.length) {
                    instance.fire("msg", "请选中一个节点")
                    return;
                }
                activeNode[0].dataset = $.extend(activeNode[0].dataset, data);
                activeNode.text(activeNode[0].dataset.name);
            }
            this.updateConnectionData = function (data) {
                var activeConn = this.getActiveConnection();
                if (!activeConn) {
                    instance.fire("msg", "请选中连接线");
                    return;
                }
                var params = activeConn.getParameters();
                params = $.extend(params, data);
                activeConn.setParameters(params);
            }
            this.applySerialno = function (nodeRows) {
                nodeRows = nodeRows || $("." + nodeRowClassName);
                for (var i = 0; i < nodeRows.length; i++) {
                    var row = nodeRows.eq(i);
                    var nodes = row.find("." + nodeClassName);
                    for (var j = 0; j < nodes.length; j++) {
                        nodes[j].dataset.serino = j;
                    }
                }
            }
            this.applyDeep = function () {
                var nodeRows = $("." + nodeRowClassName);
                for (var i = 0; i < nodeRows.length; i++) {
                    nodeRows[i].dataset["deep"] = i;
                }
            }
            this.applyConnectionData = function (connections) {
                connections = connections || instance.getAllConnections();
                for (var i = 0; i < connections.length; i++) {
                    var conn = connections[i];
                    conn.setParameter("start_node", conn.source.dataset["id"]);
                    conn.setParameter("end_node", conn.target.dataset["id"]);
                }
            }
            this.addNode = function (data) {
                var activeNode = this.getActiveNode();
                if (!activeNode.length) {
                    instance.fire("msg", "请选中一个节点")
                    return;
                }
                if (activeNode.data("type") == PROCESS_CONSTANTS.WF_NODE_TYPE_END) {
                    instance.fire("msg", "不能在结束节点下添加节点");
                    return;
                }
                var curRow = activeNode.parents("." + nodeRowClassName);
                var nextRow = curRow.next("." + nodeRowClassName);
                var deep = parseInt(nextRow.data("deep"));
                if (isEndRow(nextRow)) {
                    deep = parseInt(curRow.data("deep")) + 1;
                    nextRow = $(_newNodeRow(deep));
                    curRow.after(nextRow);
                }
    
                var default_id = "N-" + guid();
                data = $.extend({
                    deep: deep,
                    id: default_id,
                    name: "审批节点",
                    apprule: PROCESS_CONSTANTS.WF_APPRULE_HUOQIAN,
                    desc: "",
                    lparam: "",
                    selfselectapp: "0",
                    successrule: PROCESS_CONSTANTS.SUCCESS_RULE_UNANIMOUSLY,
                    type: PROCESS_CONSTANTS.WF_NODE_TYPE_TASK,
                    wparam: ""
                }, data);
                var node = _newNode(data);
                nextRow.append(node);
                _addEndpoints(instance, data);
                
                //如果只有开始结点和结束结点（加上刚才加的结点就是三个结点咯），那就自动连线
                var _Datas = this.getDatas();
                if(_Datas.nodes.length == 3){
                	var _EndNodeId = "";
                	if(_Datas.connections.length == 1){
                		var _Conn = _Datas.connections[0];
                		instance.deleteEveryConnection();
                		
                		_connect(instance, {
                			start_node: _Conn.start_node,
                			end_node: default_id,
                			start_point: _Bottom,
                			end_point: _Top,
                		});
                	}
                	if(!_EndNodeId){
                		for(var i = 0; i < _Datas.nodes.length; i++){
                			if(_Datas.nodes[i].type == PROCESS_CONSTANTS.WF_NODE_TYPE_END){
                				_EndNodeId = _Datas.nodes[i].id;
                			}
                		}
                	}

            		_connect(instance, {
            			start_node: default_id,
            			end_node: _EndNodeId,
            			start_point: _Bottom,
            			end_point: _Top,
            		});
                }
                
                instance.repaintEverything();
            }
    
            this.remove = function () {
                var node = this.getActiveNode();
                var conn = this.getActiveConnection();
                if (node.length) {
                    if (node[0].dataset.type == PROCESS_CONSTANTS.WF_NODE_TYPE_START || node[0].dataset.type == PROCESS_CONSTANTS.WF_NODE_TYPE_END) {
                        instance.fire("msg", "不能删除该节点");
                        return;
                    }
                    instance.batch(function () {
                        var dataId = _getNodeId(node[0].dataset.id);
                        //删除节点， 删除节点上所有point 和 connection
                        instance.deleteConnectionsForElement(dataId);
                        var points = instance.getEndpoints(dataId);
                        for (var i = 0; i < points.length; i++) {
                            instance.deleteEndpoint(points[i]);
                        }
                        node.remove();
                        permissionHolder.remove(dataId);
                        instance.repaintEverything();
                    });
                } else if (conn) {
                    instance.deleteConnection(conn);
                } else {
                    instance.fire("msg", "请选中要删除的元素");
                }
            }
    
            this.clear = function () {
                instance.reset();
                _initEvents();
                $("." + nodeRowClassName).remove();
            }
    
            this.zoom = function (zoom, repaintEverything) {
                if (zoom == instance.getZoom()) {
                    return false;
                }
                return instance.setZoom(zoom, repaintEverything);
            }
    
            this.move = function (direction) {
                var activeNode = this.getActiveNode();
                if (!activeNode.length) {
                    instance.fire("msg", "请选中一个节点")
                    return;
                }
                if (activeNode.data("type") == PROCESS_CONSTANTS.WF_NODE_TYPE_START || activeNode.data("type") == PROCESS_CONSTANTS.WF_NODE_TYPE_END) {
                    console.log("开始节点和结束节点不能移动");
                    return;
                }
                var targetNode = activeNode[direction == "left" ? "prev" : "next"](".node");
                if (!targetNode.length) {
                    instance.fire("msg", "无法移动");
                    return;
                }
                if (direction == "left") {
                    activeNode.after(targetNode);
                } else {
                    activeNode.before(targetNode);
                }
                instance.repaintEverything();
            }
            this.getNodeDatas = function(){
            	 var nodeDatas = [];
            	var nodes = $("." + nodeClassName);
                for (var i = 0; i < nodes.length; i++) {
                    if (nodes[i].dataset["type"] == PROCESS_CONSTANTS.WF_NODE_TYPE_END) {
                        nodes[i].dataset["deep"] = -1;
                    }
    
                    nodeDatas.push(nodes[i].dataset);
                }
                return nodeDatas;
            }
            this.getDatas = function () {
                var nodeDatas = this.getNodeDatas();
                var connDatas = [];
                this.applyDeep();
                this.applySerialno();
    
                this.applyConnectionData();
                var conns = instance.getAllConnections();
                for (var i = 0; i < conns.length; i++) {
                	var conn = conns[i];
                    var connData = conn.getParameters();
                    console.log(conns[i].endpoints);
                    if (connData.exec_condition === null || connData.exec_condition === undefined) {
                        connData.exec_condition = "";
                    }
                    connData.start_point = conn.endpoints[0].anchor.type;
                    connData.end_point = conn.endpoints[1].anchor.type;
                    connDatas.push(connData);
                }
    
                return {
                    nodes: nodeDatas,
                    connections: connDatas,
                    permissions: permissionHolder.getData()
                };
            }
        }
        var _editor = new _Editor(instance);
    
    
        function _clearElementActiveState(instance) {
            var conn = _editor.getActiveConnection();
            if (conn) {
                conn.removeType(activeConnectionTypeName);
                conn.removeClass("jtk-active");
            }
            _editor.getActiveNode().removeClass(activeNodeClassName);
        }
        /**
         * 添加节点
         * jqActiveNode: 当前选中的节点，必需，且不能是结束节点
         */
    
    
        function isStartRow(nodeRow) {
            return nodeRow.is("." + startNodeRowClassName);
        }
    
        function isEndRow(nodeRow) {
            return nodeRow.is("." + endNodeRowClassName);
        }
        //以下代码不需要jQuery 需要的放上面
        function _newNodeRow(deep) {
            var row = document.createElement("div");
            row.className = nodeRowClassName;
            if (deep == 0) {
                row.className += " " + startNodeRowClassName;
            } else if (deep == -1) {
                row.className += " " + endNodeRowClassName;
            }
            row.dataset["deep"] = deep;
            return row;
        }
    
        function _getNodeData(node) {
            return node.dataset;
        }
    
        function _newNode(data) {
            var node = document.createElement("div");
            node.className = nodeClassName;
    
            for (var k in data) {
                var v = data[k];
                node.dataset[k] = (v === null ? "" : v);
            }
            node.id = _getNodeId(data.id);
            node.innerText = data.name;
            return node;
        }
    
        function _getNodeId(dataId) {
            return "node-" + dataId;
        }
    
    
        
        function _getPoint(nodeId, d){
        	var es = instance.getEndpoints(nodeId);
        	for(var i = 0; i < es.length; i++){
        		if(es[i].anchor.type == d){
        			return es[i];
        		}
        	}
        }
    
        function _addEndpoints(instance, data) {
            var nodeId = _getNodeId(data.id);
            
            var endpointConfig = {};
            if (PROCESS_CONFIG.disabled) {
            	endpointConfig.cssClass = "visibility-hidden";
            }
            
            if (data.type == PROCESS_CONSTANTS.WF_NODE_TYPE_START) {
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Bottom",
                }, sourceEndpoint);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Right",
                }, sourceEndpoint);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Left",
                }, sourceEndpoint);
            }
            if (data.type == PROCESS_CONSTANTS.WF_NODE_TYPE_TASK) {
            	var e = $.extend({}, targetEndpoint, sourceEndpoint);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Top",
                }, e);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Bottom",
                }, e);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Right",
                }, e);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Left",
                }, e);
            }
            if (data.type == PROCESS_CONSTANTS.WF_NODE_TYPE_END) {
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Top",
                }, targetEndpoint);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Right",
                }, targetEndpoint);
                endpoint = instance.addEndpoint(nodeId, {
                    anchor: "Left",
                }, targetEndpoint);
            }

        };
     
    
        function _connect(instance, data) {
            var source = _getPoint(_getNodeId(data.start_node), data.start_point);
            var target = _getPoint(_getNodeId(data.end_node), data.end_point);
            console.log("_connect ", source, target);
            var conn = instance.connect({
                source: source
                , target: target
            });
            conn.setParameters(data);
            conn.getOverlay("label").setLabel(data.exec_condition || "");
        }
    
        function _getConnections(instance, sourceId, targetId) {
            var arr = [];
            var conns = instance.getAllConnections();
            for (var i = 0; i < conns.length; i++) {
                var conn = conns[i];
    
                var addFlag = true;
                if ((sourceId && conn.sourceId !== sourceId) || (targetId && conn.targetId !== targetId)) {
                    addFlag = false;
                }
                if (addFlag) {
                    arr.push(conn);
                }
            }
            return arr;
        }
    
        function _existConnection(instance, connection) {
            var olds = _getConnections(instance, connection.sourceId, connection.targetId);
            if (olds.length == 0) {
                return false;
            } else if (olds.length > 1) {
                console.warn("What a big bug.");
                return true;
            }
            return olds[0].id !== connection.id;
        }
    
     

        ExportObjects.helper = {
            getNodeId: _getNodeId,
            newNodeRow: _newNodeRow,
            newNode: _newNode,
            addEndpoints: _addEndpoints,
            clearElementActiveState: _clearElementActiveState,
            editor: _editor,
            _connect: _connect,
        }
        
        onJsPlumbInstanceReady(ExportObjects);
    }); 
        
}

